route.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import { getServerSideConfig } from "@/app/config/server";
  2. import {
  3. Alibaba,
  4. ALIBABA_BASE_URL,
  5. ApiPath,
  6. ModelProvider,
  7. ServiceProvider,
  8. } from "@/app/constant";
  9. import { prettyObject } from "@/app/utils/format";
  10. import { NextRequest, NextResponse } from "next/server";
  11. import { auth } from "@/app/api/auth";
  12. import { isModelAvailableInServer } from "@/app/utils/model";
  13. import type { RequestPayload } from "@/app/client/platforms/openai";
  14. const serverConfig = getServerSideConfig();
  15. async function handle(
  16. req: NextRequest,
  17. { params }: { params: { path: string[] } },
  18. ) {
  19. console.log("[Alibaba Route] params ", params);
  20. if (req.method === "OPTIONS") {
  21. return NextResponse.json({ body: "OK" }, { status: 200 });
  22. }
  23. const authResult = auth(req, ModelProvider.Qwen);
  24. if (authResult.error) {
  25. return NextResponse.json(authResult, {
  26. status: 401,
  27. });
  28. }
  29. try {
  30. const response = await request(req);
  31. return response;
  32. } catch (e) {
  33. console.error("[Alibaba] ", e);
  34. return NextResponse.json(prettyObject(e));
  35. }
  36. }
  37. export const GET = handle;
  38. export const POST = handle;
  39. export const runtime = "edge";
  40. export const preferredRegion = [
  41. "arn1",
  42. "bom1",
  43. "cdg1",
  44. "cle1",
  45. "cpt1",
  46. "dub1",
  47. "fra1",
  48. "gru1",
  49. "hnd1",
  50. "iad1",
  51. "icn1",
  52. "kix1",
  53. "lhr1",
  54. "pdx1",
  55. "sfo1",
  56. "sin1",
  57. "syd1",
  58. ];
  59. async function request(req: NextRequest) {
  60. const controller = new AbortController();
  61. // alibaba use base url or just remove the path
  62. let path = `${req.nextUrl.pathname}`.replaceAll(
  63. ApiPath.Alibaba + "/" + Alibaba.ChatPath,
  64. "",
  65. );
  66. let baseUrl = serverConfig.alibabaUrl || ALIBABA_BASE_URL;
  67. if (!baseUrl.startsWith("http")) {
  68. baseUrl = `https://${baseUrl}`;
  69. }
  70. if (baseUrl.endsWith("/")) {
  71. baseUrl = baseUrl.slice(0, -1);
  72. }
  73. console.log("[Proxy] ", path);
  74. console.log("[Base Url]", baseUrl);
  75. const timeoutId = setTimeout(
  76. () => {
  77. controller.abort();
  78. },
  79. 10 * 60 * 1000,
  80. );
  81. const fetchUrl = `${baseUrl}${path}`;
  82. const clonedBody = await req.text();
  83. const { messages, model, stream, top_p, ...rest } = JSON.parse(
  84. clonedBody,
  85. ) as RequestPayload;
  86. const requestBody = {
  87. model,
  88. input: {
  89. messages,
  90. },
  91. parameters: {
  92. ...rest,
  93. top_p: top_p === 1 ? 0.99 : top_p, // qwen top_p is should be < 1
  94. result_format: "message",
  95. incremental_output: true,
  96. },
  97. };
  98. const fetchOptions: RequestInit = {
  99. headers: {
  100. "Content-Type": "application/json",
  101. Authorization: req.headers.get("Authorization") ?? "",
  102. "X-DashScope-SSE": stream ? "enable" : "disable",
  103. },
  104. method: req.method,
  105. body: JSON.stringify(requestBody),
  106. redirect: "manual",
  107. // @ts-ignore
  108. duplex: "half",
  109. signal: controller.signal,
  110. };
  111. // #1815 try to refuse some request to some models
  112. if (serverConfig.customModels && req.body) {
  113. try {
  114. // not undefined and is false
  115. if (
  116. isModelAvailableInServer(
  117. serverConfig.customModels,
  118. model as string,
  119. ServiceProvider.Alibaba as string,
  120. )
  121. ) {
  122. return NextResponse.json(
  123. {
  124. error: true,
  125. message: `you are not allowed to use ${model} model`,
  126. },
  127. {
  128. status: 403,
  129. },
  130. );
  131. }
  132. } catch (e) {
  133. console.error(`[Alibaba] filter`, e);
  134. }
  135. }
  136. console.log("[Alibaba request]", fetchOptions.headers, req.method);
  137. try {
  138. const res = await fetch(fetchUrl, fetchOptions);
  139. console.log("[Alibaba response]", res.status, " ", res.headers, res.url);
  140. // to prevent browser prompt for credentials
  141. const newHeaders = new Headers(res.headers);
  142. newHeaders.delete("www-authenticate");
  143. // to disable nginx buffering
  144. newHeaders.set("X-Accel-Buffering", "no");
  145. return new Response(res.body, {
  146. status: res.status,
  147. statusText: res.statusText,
  148. headers: newHeaders,
  149. });
  150. } finally {
  151. clearTimeout(timeoutId);
  152. }
  153. }